package com.iot.ammeter.view.custom

import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.text.TextPaint
import android.util.AttributeSet
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.res.getResourceIdOrThrow
import com.google.android.material.card.MaterialCardView
import com.iot.ammeter.R
import java.lang.IllegalArgumentException

/**
 * 用于显示我的设备相关信息，可根据自定义配置在 xml 调整卡片和文字样式。
 *
 * 文字绘制位置为卡片中间，通过 interval 来调整于中线的距离，其它属性委托给 [MaterialCardView]
 */
class InfoCard(context: Context, attrs: AttributeSet) : MaterialCardView(context, attrs) {

    // text paints
    private val titlePaint: Paint
    private val contentPaint: TextPaint
    private val hintTagPaint: Paint

    /**
     * 自定义属性，参考属性文件
     * @see R.styleable.InfoCard
     * @see setHelper
     */
    // 以下几个自定义属性可供外部访问，实际值存放于 back field
    // back field 由 view 创建时在 init 块进行初始化
    private var _title: String
    var title: String
        get() = _title
        set(value) = setHelper(_title != value) {
            _title = value
        }

    private var _content: String
    var content: String
        get() = _content
        set(value) = setHelper(_content != value) {
            _content = value
        }

    private var _hintTag: String
    var hintTag: String
        get() = _hintTag
        set(value) = setHelper(_hintTag != value) {
            _hintTag = value
        }

    private var _interval: Float
    var interval: Float
        get() = _interval
        set(value) = setHelper(_interval != value) {
            _interval = value
        }


    private var _iconColor: Int
    var iconColor: Int
        get() = _iconColor
        set(value) {
            val color = context.getColor(value)
            setHelper(_iconColor != value) {
                _iconColor = color
            }
        }

    private var drawable: Drawable?

    private val textAlign: Int
    private val edgeDistance: Float

    private var hintSpace: Float

    init {
        // 获取自定义属性并进行存储，paint 相关属性直接设置到 paint 中
        context.obtainStyledAttributes(attrs, R.styleable.InfoCard).also {
            _title = it.getString(R.styleable.InfoCard_titleText) ?: ""
            _content = it.getString(R.styleable.InfoCard_contentText) ?: ""
            _hintTag = it.getString(R.styleable.InfoCard_hintTagText) ?: ""
            drawable = try {
                AppCompatResources.getDrawable(context, it.getResourceIdOrThrow(R.styleable.InfoCard_iconRes))
            } catch (e: Exception) {
                null
            }

            val primaryTextColor = context.getColor(R.color.md_theme_light_secondary)
            val secondTextColor = context.getColor(R.color.md_theme_light_secondary)

            titlePaint = Paint().apply {
                textSize = it.getDimension(R.styleable.InfoCard_titleFontSize, 16F)
                color = it.getColor(R.styleable.InfoCard_titleColor, primaryTextColor)
                typeface = Typeface.DEFAULT_BOLD
            }

            contentPaint = TextPaint().apply {
                textSize = it.getDimension(R.styleable.InfoCard_contentFontSize, 16F)
                color = it.getColor(R.styleable.InfoCard_contentColor, primaryTextColor)
            }

            hintTagPaint = Paint().apply {
                val hintFontSize = it.getDimension(R.styleable.InfoCard_hintTagFontSize, 12F)
                hintSpace = hintFontSize * 0.8F
                textSize = hintFontSize
                color = it.getColor(R.styleable.InfoCard_hintTagColor, secondTextColor)
            }

            _interval = it.getDimension(R.styleable.InfoCard_interval, 10F)
            textAlign = it.getInt(R.styleable.InfoCard_textAlign, 0)
            edgeDistance = it.getDimension(R.styleable.InfoCard_edgeDistance, 20F)
            // icon color
            _iconColor = it.getColor(R.styleable.InfoCard_iconColor, context.getColor(R.color.md_theme_light_primary))
        }.recycle()
    }

    /**
     * 属性更新 helper：用于在更新自定义属性成员后调用 [invalidate] 重新绘制 view
     * @param invalidate 是否重新绘制
     * @param set() 更新方法
     */
    private fun setHelper(invalidate: Boolean, set: () -> Unit) {
        set()
        if (invalidate) invalidate()
    }

    /**
     * 更新 title 颜色
     * @see setHelper
     */
    fun titleColor(color: Int) = setHelper(titlePaint.color != color) {
        titlePaint.color = context.getColor(color)
    }

    /**
     * 更新 content 颜色
     * @see setHelper
     */
    fun contentColor(color: Int) = setHelper(true) {
        contentPaint.color = context.getColor(color)
    }

    /**
     * 更新 hint tag 颜色
     * @see setHelper
     */
    fun hintTagColor(color: Int) = setHelper(hintTagPaint.color != color) {
        hintTagPaint.color = context.getColor(color)
    }

    /**
     * 更新 title 字体大小
     * 未实验 / 未做单位转换
     * @see setHelper
     */
    fun titleFontSize(textSize: Float) = setHelper(titlePaint.textSize != textSize) {
        titlePaint.textSize = textSize
    }

    /**
     * 更新 content 字体大小
     * 未实验 / 未做单位转换
     * @see setHelper
     */
    fun contentFontSize(textSize: Float) = setHelper(contentPaint.textSize != textSize) {
        contentPaint.textSize = textSize
    }

    /**
     * 更新 hint tag 字体大小
     * 未实验 / 未做单位转换
     * @see setHelper
     */
    fun hintTagFontSize(textSize: Float) = setHelper(hintTagPaint.textSize != textSize) {
        hintTagPaint.textSize = textSize
        hintSpace = textSize * 2
    }

    /**
     * 设置 icon 图标资源
     */
    fun icon(iconResId: Int) {
        drawable = AppCompatResources.getDrawable(context, iconResId)
        invalidate()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        // 计算 title 和 content 的水平绘制起点
        val titleCoordinateX = getTextXCoordinate(titlePaint.measureText(title))

        // title 的 y-coordinate 为距离卡片水平中线以上 interval 间距
        val titleCoordinateY = height / 2F - interval

        // 根据文本宽度计算 x-coordinate，使得 title 和 content 文本绘制水平居中
        canvas.drawText(title, titleCoordinateX, titleCoordinateY, titlePaint)

        // content 的 y-coordinate 为距离卡片水平中线以下 (interval + 文本高度)
        // 即 content 文本顶部距离水平中线为 interval 间距
        // 多行文本时，每一行拥有一定间隔
        val contentTextHeight = contentPaint.let {
            it.descent() - it.ascent()
        }

        var coordinateY = height / 2F + interval
        var hintTageCoordinateX = 0F

        content.lines().forEach {
            // 计算 content 本行文本的 y-coordinate
            coordinateY += contentTextHeight + 10
            val measure = contentPaint.measureText(it)
            val coordinateX = getTextXCoordinate(measure)
            // 计算 content 最右边边缘位置 x 坐标
            if (coordinateX + measure > hintTageCoordinateX)
                hintTageCoordinateX = coordinateX + measure
            canvas.drawText(it, coordinateX, coordinateY, contentPaint)
        }

        // hintTag 绘制在 content 右边
        canvas.drawText(hintTag, hintTageCoordinateX + hintSpace, coordinateY, hintTagPaint)

        if (textAlign == -1) {
            drawable?.run {
                setBounds(
                    width - minimumWidth - edgeDistance.toInt(),
                    (height - minimumHeight) / 2,
                    width - edgeDistance.toInt(),
                    (height + minimumHeight) / 2
                )
                setTint(iconColor)
                draw(canvas)
            }
        }
    }

    /**
     * 根据 textAlign 和文本宽度获取对应的 x-coordinate
     */
    private fun getTextXCoordinate(measure: Float): Float = when (textAlign) {
        0 -> (width - measure) / 2
        1 -> width - measure - edgeDistance
        -1 -> edgeDistance
        else -> throw IllegalArgumentException("${this::class.java.name} -> 非法 textAlign 参数：${textAlign}")
    }
}
